-module(rtps_frag).

%% Fragmentation experimental code: IGNORE

-export([t/1, t/2, max_size/0, data_msg/2, df_size/1, inline_qos/0]).

-include("rtps.hrl").
-include("rtps_psm.hrl").


%% dataSize: The total size in bytes of the original data before
%% fragmentation.
-define(Sample_size, 1512).
%% fragmentSize: The size of an individual fragment in bytes. The maximum fragment
%% size equals 64K.
-define(Fragment_size, 512). 
%% The following two sizes are the overhead needed for a data and
%% data_frag message respectively without InlineQos.
-define(Data_msg_min_size, 20).
-define(Data_frag_min_size, 32).

t(Size) ->
    t(Size, undefined).

%% Make the decision for fragmentation.
t(Sample_size, Inline_qos) ->
    Payload = <<0:Sample_size/unit:8>>,
    %% How much room is available in a single RTPS message: i.e. the
    %% room available by the transport medium minus the RTPS message
    %% overhead.
    Max_size = max_size(),
    Inline_qos_size = case Inline_qos of
			  undefined -> 0;
			  _ -> byte_size(rtps_psm:enc_parl(Inline_qos))
		      end,
    % Check to see if fragmentation is required
    case Max_size >= ?Data_msg_min_size + Inline_qos_size + Sample_size of
	true ->
	    %% No problem, sample can be handled as a regular data submessage
	    data_msg(Payload, Inline_qos);
	false ->
	    %% Not enough room

	    %% If the Inline_qos is very large, there is a chance
	    %% there is not enough room to also hold the actual data,
	    %% which can be solved by using fragmentation in which
	    %% case the first data_frag message contains the
	    %% Inline_qos, which leaves room to add the data as
	    %% fragments to the data_frag message or even move all
	    %% data to the following data frag message. However,
	    %% according to the specs, the inline_qos should be
	    %% included with the first data_frag message with fragment
	    %% number 1, which means that is the inline_qos is to big
	    %% to also leave room for a single fragment, there is no
	    %% way we can hanlde the message.
	    case Max_size >= ?Data_frag_min_size + Inline_qos_size + ?Fragment_size of
		true ->
		    %% There is room for at least one fragment and the
		    %% InlineQos in the first data_frag submessage,
		    %% which is a minimum requirement according to the
		    %% specs: i.e. InlineQos must be included in the
		    %% first data_frag message as described in "For a
		    %% given sequence number, if in-line QoS
		    %% parameters are used, they must be included with
		    %% the first DataFrag Submessage (containing the
		    %% fragment with fragment number equal to 1)."
		    %% (8.4.14.1.2 How to send fragments, p120).
		    data_frag(Sample_size, Max_size, ?Fragment_size, Payload, Inline_qos);
		false ->
		    {error, size_error}
	    end
    end.


data_frag(Sample_size, Max_size, Fragment_size, Payload, Inline_qos) ->
    %% Process the first data_frag message with the Inline_qos if any.
    Inline_qos_size = case Inline_qos of
			  undefined -> 0;
			  _ -> byte_size(rtps_psm:enc_parl(Inline_qos))
		      end,
    Available_size = Max_size - ?Data_frag_min_size - Inline_qos_size,
    Frags_in_msg = Available_size div Fragment_size, % fragments_in_this submessage
    N = Frags_in_msg * Fragment_size,
    <<Blob:N/bytes, Rest/binary>> = Payload,
    Data_frag = #data_frag{fragment_starting_num = 1, fragments_in_submessage = Frags_in_msg,
			   fragment_size = Fragment_size, sample_size = Sample_size, 
			   inline_qos = Inline_qos, serialized_payload = Blob},
    %% Now prepare for the rest of the data_frag messages
    Data_frag_tmpl = #data_frag{fragment_size = Fragment_size, sample_size = Sample_size, 
				inline_qos = undefined},
    Number_of_frags = ((Sample_size + Fragment_size - 1) div Fragment_size) - Frags_in_msg,
    Frags_per_msg = (Max_size - ?Data_frag_min_size) div Fragment_size,
    Starting_num = 1 + Frags_in_msg,
    data_frag(Number_of_frags, Frags_per_msg, Rest, Starting_num, Data_frag_tmpl, [Data_frag]).

data_frag(Number_of_frags, Frags_per_msg, Payload, Starting_num, Data_frag_tmpl, Acc)
  when Number_of_frags =< Frags_per_msg ->
    Data_frag = Data_frag_tmpl#data_frag{fragment_starting_num = Starting_num,
					 fragments_in_submessage = Number_of_frags,
					 serialized_payload = Payload},
    Last_fragment_num = Starting_num + Number_of_frags - 1,
    Heartbeat_frag = #heartbeat_frag{last_fragment_num = Last_fragment_num, count = 1},
    lists:reverse([Heartbeat_frag, Data_frag | Acc]);
data_frag(Number_of_frags, Frags_per_msg, Payload, Starting_num, Data_frag_tmpl, Acc) ->
    N = Frags_per_msg * Data_frag_tmpl#data_frag.fragment_size,
    <<Part:N/bytes, Rest/binary>> = Payload,
    Data_frag = Data_frag_tmpl#data_frag{fragment_starting_num = Starting_num,
					 fragments_in_submessage = Frags_per_msg,
					 serialized_payload = Part},
    Number_of_frags1 = Number_of_frags - Frags_per_msg,
    Starting_num1 = Starting_num + Frags_per_msg,
    Acc1 = [Data_frag | Acc],
    data_frag(Number_of_frags1, Frags_per_msg, Rest, Starting_num1, Data_frag_tmpl, Acc1).
    
max_size() ->
    Max_msg_size = rtps_psm:size(max_msg, ?MTU),
    Msg_header_size = rtps_psm:size(msg_header),
    Submsg_header_size = rtps_psm:size(submsg_header),
    Data_frag_min_size = df_size(undefined),
    Max_msg_size - Msg_header_size - Submsg_header_size - Data_frag_min_size.

df_size(Inline_qos) ->
    Data_frag = #data_frag{key_flag = false, reader_id = unknown, writer_id = unknown,
			   writer_sn = 1, fragment_starting_num = 1, fragments_in_submessage = 0,
			   fragment_size = 1024, sample_size = 0, inline_qos = Inline_qos,
			   serialized_payload = <<>>},
    #submsg{length = Length} = rtps_psm:enc_submsg(Data_frag),
    Length.

inline_qos() ->
    [#parameter{id = expects_inline_qos, value = true},
     #parameter{id = user_data, value = <<0:700/unit:8>>},
     #parameter{id = participant_lease_duration, value = 10000},
     #parameter{id = participant_manual_liveliness_count, value = 10}].

data_msg(Payload, Inline_qos) ->
    #data_msg{reader_id = unknown, writer_id = unknown, writer_sn = 1, 
	      inline_qos = Inline_qos,
	      serialized_key = undefined,
	      serialized_data = Payload}.
